classdef VideoImagesToBvhConverter < handle
% Generates Bvh files from VideoImageCollection by pose estimation.
	properties
	destFile;  % destination BVH file
	model;  % skeleton model definition in BVH file
	originalPosStr;  % X, Y, Z coordinate string of root at the 1st frame
	nCameras;
	frameTime;
	end

	properties (Access = private)
	strCache = cell(100, 1);  % preallocation
	frameCounter;
	rotationStrFunc;
	end

	methods (Static)
	function model = getDefaultModel()
		persistent defaultModel;
		if isempty(defaultModel)
			defaultModel = fileread('defaultModel.txt');
		end
		model = defaultModel;
	end
	end
	%----------------8<----------------%
	methods
	function obj = VideoImagesToBvhConverter(destFile, varargin)
		p = inputParser;
		addRequired(p, 'destFile', @isstr);
		addParamValue(p, 'model', ...
			VideoImagesToBvhConverter.getDefaultModel(), @isstr);
		% defined in `01_01.bvh`, corresponding to defaultModel
		addParamValue(p, 'originalPosStr', '9.3722 17.8693 -17.3198', @isstr);
		addParamValue(p, 'frameTime', .383333, @isnumeric);
		addParamValue(p, 'nCameras', 24, @isnumeric);
		parse(p, destFile, varargin{:});

		obj.destFile = p.Results.destFile;
		if isempty(obj.destFile)
			error('Invalid destination file');
		end

		obj.model = p.Results.model;
		obj.originalPosStr = strtrim(p.Results.originalPosStr);
		obj.frameTime = p.Results.frameTime;
		obj.nCameras = round(p.Results.nCameras);
		obj.rotationStrFunc = @(camID) sprintf(' 0 %.4f 0', ...
			360 / obj.nCameras * camID);
		obj.frameCounter = 0;
	end

	function [] = fromFile(obj, filePath, frame, camID)
		fid = fopen(filePath, 'r');
		while ~strcmp(fgetl(fid), 'MOTION')
			% loops until 'MOTION' has been read
		end
		lineStartsWith = @(s) strncmp(fgetl(fid), s, length(s));
		assert(lineStartsWith('Frames:'));
		assert(lineStartsWith('Frame Time:'));

		for i = 1:frame
			numStr = fgetl(fid);
			assert(~isempty(numStr));
		end
		fclose(fid);
		
		matched = regexp(numStr, ...
			'((\s)+)?-?[\.\d]+\s+-?[\.\d]+\s+-?[\.\d]+(?<rest>.+)', 'names');
			
		counter = obj.frameCounter + 1;
		obj.frameCounter = counter;
		% [position_of_Null 0 YRotation 0 rest]
		obj.strCache{counter} = [obj.originalPosStr ...
			obj.rotationStrFunc(camID) matched.rest];

		% dynamically increases capacity by 50 percent
		if counter == size(obj.strCache, 1)
			obj.strCache{ceil(counter * 1.5)} = [];
		end
	end

	function flush(obj)
		destFileID = fopen(obj.destFile, 'w');
		% writes bvh head
		fprintf(destFileID, obj.model, obj.frameCounter, obj.frameTime);
		% writes all frames
		fprintf(destFileID, '%s\n', obj.strCache{1:obj.frameCounter});
		fclose(destFileID);
	end
	end
end
